﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;

public static class DpiHelper
{
    private const double LogicalDpi = 96;

    private static MatrixTransform transformFromDevice;

    private static MatrixTransform transformToDevice;

    //public static double DeviceDpiX
    //{
    //    get
    //    {
    //        return DpiHelper.<DeviceDpiX>k__BackingField;
    //    }
    //    private set
    //    {
    //        DpiHelper.<DeviceDpiX>k__BackingField = value;
    //    }
    //}

    public static double DeviceDpiX { get; set; }

    //public static double DeviceDpiY
    //{
    //    get
    //    {
    //        return DpiHelper.<DeviceDpiY>k__BackingField;
    //    }
    //    private set
    //    {
    //        DpiHelper.<DeviceDpiY>k__BackingField = value;
    //    }
    //}
    public static double DeviceDpiY { get; set; }

    public static double DeviceToLogicalUnitsScalingFactorX
    {
        get
        {
            Matrix matrix = DpiHelper.TransformFromDevice.Matrix;
            return matrix.M11;
        }
    }

    public static double DeviceToLogicalUnitsScalingFactorY
    {
        get
        {
            Matrix matrix = DpiHelper.TransformFromDevice.Matrix;
            return matrix.M22;
        }
    }

    public static double LogicalToDeviceUnitsScalingFactorX
    {
        get
        {
            Matrix matrix = DpiHelper.TransformToDevice.Matrix;
            return matrix.M11;
        }
    }

    public static double LogicalToDeviceUnitsScalingFactorY
    {
        get
        {
            Matrix matrix = DpiHelper.TransformToDevice.Matrix;
            return matrix.M22;
        }
    }

    public static MatrixTransform TransformFromDevice
    {
        get
        {
            return DpiHelper.transformFromDevice;
        }
    }

    public static MatrixTransform TransformToDevice
    {
        get
        {
            return DpiHelper.transformToDevice;
        }
    }

    static DpiHelper()
    {
        IntPtr dC = NativeMethods.GetDC(IntPtr.Zero);
        if (dC == IntPtr.Zero)
        {
            DpiHelper.DeviceDpiX = 96;
            DpiHelper.DeviceDpiY = 96;
        }
        else
        {
            DpiHelper.DeviceDpiX = (double)NativeMethods.GetDeviceCaps(dC, 88);
            DpiHelper.DeviceDpiY = (double)NativeMethods.GetDeviceCaps(dC, 90);
            NativeMethods.ReleaseDC(IntPtr.Zero, dC);
        }
        Matrix identity = Matrix.Identity;
        Matrix matrix = Matrix.Identity;
        identity.Scale(DpiHelper.DeviceDpiX / 96, DpiHelper.DeviceDpiY / 96);
        matrix.Scale(96 / DpiHelper.DeviceDpiX, 96 / DpiHelper.DeviceDpiY);
        DpiHelper.transformFromDevice = new MatrixTransform(matrix);
        DpiHelper.transformFromDevice.Freeze();
        DpiHelper.transformToDevice = new MatrixTransform(identity);
        DpiHelper.transformToDevice.Freeze();
    }

    public static Point DeviceToLogicalUnits(this Point devicePoint)
    {
        return DpiHelper.TransformFromDevice.Transform(devicePoint);
    }

    public static Rect DeviceToLogicalUnits(this Rect deviceRect)
    {
        Rect rect = deviceRect;
        rect.Transform(DpiHelper.TransformFromDevice.Matrix);
        return rect;
    }

    public static Size DeviceToLogicalUnits(this Size deviceSize)
    {
        return new Size(deviceSize.Width * DpiHelper.DeviceToLogicalUnitsScalingFactorX, deviceSize.Height * DpiHelper.DeviceToLogicalUnitsScalingFactorY);
    }

    public static Size GetDeviceActualSize(this FrameworkElement element)
    {
        Size size = new Size(element.ActualWidth, element.ActualHeight);
        return size.LogicalToDeviceUnits();
    }

    public static double GetDeviceHeight(this Window window)
    {
        return window.Height * DpiHelper.LogicalToDeviceUnitsScalingFactorY;
    }

    public static double GetDeviceLeft(this Window window)
    {
        return window.Left * DpiHelper.LogicalToDeviceUnitsScalingFactorX;
    }

    public static Rect GetDeviceRect(this Window window)
    {
        RECT rECT;
        NativeMethods.GetWindowRect((new WindowInteropHelper(window)).Handle, out rECT);
        return new Rect(new Point((double)rECT.Left, (double)rECT.Top), new Size((double)rECT.Width, (double)rECT.Height));
    }

    public static double GetDeviceTop(this Window window)
    {
        return window.Top * DpiHelper.LogicalToDeviceUnitsScalingFactorY;
    }

    public static double GetDeviceWidth(this Window window)
    {
        return window.Width * DpiHelper.LogicalToDeviceUnitsScalingFactorX;
    }

    public static Point LogicalToDeviceUnits(this Point logicalPoint)
    {
        return DpiHelper.TransformToDevice.Transform(logicalPoint);
    }

    public static Rect LogicalToDeviceUnits(this Rect logicalRect)
    {
        Rect rect = logicalRect;
        rect.Transform(DpiHelper.TransformToDevice.Matrix);
        return rect;
    }

    public static Size LogicalToDeviceUnits(this Size logicalSize)
    {
        return new Size(logicalSize.Width * DpiHelper.LogicalToDeviceUnitsScalingFactorX, logicalSize.Height * DpiHelper.LogicalToDeviceUnitsScalingFactorY);
    }

    public static void SetDeviceHeight(this Window window, double deviceHeight)
    {
        window.Height = deviceHeight * DpiHelper.DeviceToLogicalUnitsScalingFactorY;
    }

    public static void SetDeviceLeft(this Window window, double deviceLeft)
    {
        window.Left = deviceLeft * DpiHelper.DeviceToLogicalUnitsScalingFactorX;
    }

    public static void SetDeviceTop(this Window window, double deviceTop)
    {
        window.Top = deviceTop * DpiHelper.DeviceToLogicalUnitsScalingFactorY;
    }

    public static void SetDeviceWidth(this Window window, double deviceWidth)
    {
        window.Width = deviceWidth * DpiHelper.DeviceToLogicalUnitsScalingFactorX;
    }
}
